package com.htd.erp.web;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;

import javax.enterprise.context.Conversation;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;

import com.htd.erp.common.DataRepository;
import com.htd.erp.model.AbstractEntity;

/**
 * @author Zhang Guilin
 * 
 */
public abstract class AbstractEntitySuggester<T extends AbstractEntity> implements Suggester<T> {

	private static final long serialVersionUID = 8905718965913478429L;

	@DataRepository
	@Inject
	protected EntityManager entityManager;

	@Inject
	private Conversation conversation;

	protected List<T> list;

	abstract protected Class<T> getEntityClass();

	abstract protected String getFieldName();

	@Override
	public List<T> getList() {
		return list;
	}

	@Override
	public List<String> autocomplete(String searchString) {
		startConversation();
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(getEntityClass());
		Root<T> root = criteriaQuery.from(getEntityClass());
		Path<String> expression = root.get(getFieldName());
		Expression<Integer> locator = criteriaBuilder.locate(criteriaBuilder.lower(expression), StringUtils.lowerCase(searchString), 1);
		criteriaQuery.where(criteriaBuilder.gt(locator, 0));
		list = entityManager.createQuery(criteriaQuery).getResultList();
		return convertToStringList(list);
	}

	@Override
	public T getAsObject(FacesContext context, UIComponent component, String string) {
		if(string == null || list == null) {
			return null;
		}
		for (T item : list) {
			if (StringUtils.equals(convertToString(item), string)) {
				return item;
			}
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public String getAsString(FacesContext context, UIComponent component, Object object) {
		return convertToString((T) object);
	}

	protected List<String> convertToStringList(List<T> entities) {
		List<String> result = new ArrayList<String>(entities.size());
		for (T t : entities) {
			result.add(convertToString(t));
		}
		return result;
	}

	protected String convertToString(T t) {
		if (t == null) {
			return null;
		}
		try {
			return new PropertyDescriptor(getFieldName(), getEntityClass()).getReadMethod().invoke(t).toString();
		} catch (Exception e) {
			throw new IllegalStateException("Can not read field " + getFieldName());
		}
	}

	protected void startConversation() {
		if (conversation.isTransient()) {
			conversation.begin();
		}
	}
}
